﻿namespace OasisCiq.Xml
{
    // ReSharper disable MemberCanBePrivate.Global

    using System;
    using System.Collections.Generic;
    using System.Xml;
    using CommonTypes;
    using External;
    using Xal;

    public class XalXmlReader
    {
        private const string XmlNS = XmlNamespaces.Address;
        private readonly XmlReader reader;
        private readonly CommonTypesXmlReader ctReader = new CommonTypesXmlReader();
        private readonly XLinkXmlReader xLinkReader = new XLinkXmlReader();

        public XalXmlReader(XmlReader reader)
        {
            this.reader = reader;
        }

        public AddressType ReadAddress()
        {
            return ReadAddress("Address", XmlNS);
        }

        public AddressType ReadAddress(string elementName, string elementNamespace)
        {
            if (!CheckElement(elementName, elementNamespace))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new AddressType();
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ctReader.ReadValidityDateGroup(element, attrs);
            attrs = ctReader.ReadLanguageCodeGroup(element, attrs);
            attrs = xLinkReader.Read(element, attrs);
            attrs = ReadAddressAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            if (!reader.IsEmptyElement)
            {
                reader.Read(); // to first child
                element.FreeTextAddress = ReadFreeTextAddress();
                element.Country = ReadCountry();
                element.AdministrativeArea = ReadAdministrativeArea();
                element.Locality = ReadLocality();
                element.Thoroughfare = ReadThoroughfare();
                element.Premises = ReadPremises();
                element.PostCode = ReadPostCode();
                element.RuralDelivery = ReadRuralDelivery();
                element.PostalDeliveryPoint = ReadPostalDeliveryPoint();
                element.PostOffice = ReadPostOffice();
                element.GeoRss = ReadGeoRss();
                element.LocationByCoordinates = ReadLocationByCoordinates();
            }

            reader.Read(); // from a:Address empty or end element to next node
            return element;
        }

        public FreeTextAddress ReadFreeTextAddress()
        {
            if (!CheckElement("FreeTextAddress"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new FreeTextAddress();
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to a:AddressLine
            while (true)
            {
                var addressLine = ReadAddressLine();
                if (addressLine == null)
                {
                    break;
                }

                if (element.AddressLines == null)
                {
                    element.AddressLines = new List<AddressLine>();
                }

                element.AddressLines.Add(addressLine);
            }

            reader.Read(); // from a:FreeTextAddress end element to next node
            return element;
        }

        public AddressLine ReadAddressLine()
        {
            if (!CheckElement("AddressLine"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var value = reader.ReadElementContentAsString();
            var element = new AddressLine(value);
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ReadAddressLineAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            return element;
        }

         public CountryType ReadCountry()
         {
             return ReadCountry(new CountryType(), "Country", XmlNS);
         }

         public CountryType ReadCountry(CountryType element, string elementName, string elementNamespace)
        {
            if (!CheckElement(elementName, elementNamespace))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to a:NameElement
            while (true)
            {
                var nameElement = ReadCountryNameElement();
                if (nameElement == null)
                {
                    break;
                }

                if (element.NameElements == null)
                {
                    element.NameElements = new List<CountryNameElement>();
                }

                element.NameElements.Add(nameElement);
            }

            reader.Read(); // from a:Country end element to next node
            return element;
        }

        public CountryNameElement ReadCountryNameElement()
        {
            if (!CheckElement("NameElement"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var value = reader.ReadElementContentAsString();
            var element = new CountryNameElement(value);
            attrs = ctReader.ReadAbbreviationGroup(element, attrs);
            attrs = ReadCountryNameElementAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            return element;
        }

        public AdministrativeArea ReadAdministrativeArea()
        {
            if (!CheckElement("AdministrativeArea"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new AdministrativeArea();
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ReadAdministrativeAreaAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to a:NameElement
            while (true)
            {
                var nameElement = ReadAdministrativeAreaNameElement();
                if (nameElement == null)
                {
                    break;
                }

                if (element.NameElements == null)
                {
                    element.NameElements = new List<AdministrativeAreaNameElement>();
                }

                element.NameElements.Add(nameElement);
            }

            element.SubAdministrativeArea = ReadSubAdministrativeArea();
            reader.Read(); // from a:AdministrativeArea end element to next node
            return element;
        }

        public AdministrativeAreaNameElement ReadAdministrativeAreaNameElement()
        {
            if (!CheckElement("NameElement"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var value = reader.ReadElementContentAsString();
            var element = new AdministrativeAreaNameElement(value);
            attrs = ctReader.ReadAbbreviationGroup(element, attrs);
            attrs = ReadAdministrativeAreaNameElementAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            return element;
        }

        public SubAdministrativeArea ReadSubAdministrativeArea()
        {
            if (!CheckElement("SubAdministrativeArea"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new SubAdministrativeArea();
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ReadSubAdministrativeAreaAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to a:NameElement
            while (true)
            {
                var nameElement = ReadSubAdministrativeAreaNameElement();
                if (nameElement == null)
                {
                    break;
                }

                if (element.NameElements == null)
                {
                    element.NameElements = new List<SubAdministrativeAreaNameElement>();
                }

                element.NameElements.Add(nameElement);
            }

            reader.Read(); // from a:SubAdministrativeArea end element to next node
            return element;
        }

        public SubAdministrativeAreaNameElement ReadSubAdministrativeAreaNameElement()
        {
            if (!CheckElement("NameElement"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var value = reader.ReadElementContentAsString();
            var element = new SubAdministrativeAreaNameElement(value);
            attrs = ctReader.ReadAbbreviationGroup(element, attrs);
            attrs = ReadSubAdministrativeAreaNameElementAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            return element;
        }

        public Locality ReadLocality()
        {
            if (!CheckElement("Locality"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new Locality();
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ReadLocalityAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to a:NameElement
            while (true)
            {
                var nameElement = ReadLocalityNameElement();
                if (nameElement == null)
                {
                    break;
                }

                if (element.NameElements == null)
                {
                    element.NameElements = new List<LocalityNameElement>();
                }

                element.NameElements.Add(nameElement);
            }

            element.SubLocality = ReadSubLocality();
            reader.Read(); // from a:Locality end element to next node
            return element;
        }

        public LocalityNameElement ReadLocalityNameElement()
        {
            if (!CheckElement("NameElement"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var value = reader.ReadElementContentAsString();
            var element = new LocalityNameElement(value);
            attrs = ctReader.ReadAbbreviationGroup(element, attrs);
            attrs = ReadLocalityNameElementAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            return element;
        }

        public SubLocality ReadSubLocality()
        {
            if (!CheckElement("SubLocality"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new SubLocality();
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ReadSubLocalityAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to a:NameElement
            while (true)
            {
                var nameElement = ReadSubLocalityNameElement();
                if (nameElement == null)
                {
                    break;
                }

                if (element.NameElements == null)
                {
                    element.NameElements = new List<SubLocalityNameElement>();
                }

                element.NameElements.Add(nameElement);
            }

            reader.Read(); // from a:SubLocality end element to next node
            return element;
        }

        public SubLocalityNameElement ReadSubLocalityNameElement()
        {
            if (!CheckElement("NameElement"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var value = reader.ReadElementContentAsString();
            var element = new SubLocalityNameElement(value);
            attrs = ctReader.ReadAbbreviationGroup(element, attrs);
            attrs = ReadSubLocalityNameElementAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            return element;
        }

        public Thoroughfare ReadThoroughfare()
        {
            if (!CheckElement("Thoroughfare"))
            {
                return null;
            }

            var element = (Thoroughfare)ReadThoroughfareType(new Thoroughfare());
            while (CheckElement("SubThoroughfare"))
            {
                var subElement = ReadThoroughfareType(new ThoroughfareType());
                if (element.SubThoroughfares == null)
                {
                    element.SubThoroughfares = new List<ThoroughfareType>();
                }

                element.SubThoroughfares.Add(subElement);
            }

            if (element.SubThoroughfares != null && element.SubThoroughfares.Count > 0)
            {
                reader.Read(); // from a:Thoroughfare end element to next node
            }

            return element;
        }

        public ThoroughfareType ReadThoroughfareType(ThoroughfareType element)
        {
            if (!CheckElement("Thoroughfare") && !CheckElement("SubThoroughfare"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ReadThoroughfareTypeAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to a:NameElement or a:Number
            while (true)
            {
                var nameElement = ReadThoroughfareNameElement();
                if (nameElement != null)
                {
                    if (element.NameElements == null)
                    {
                        element.NameElements = new List<ThoroughfareNameElement>();
                    }

                    element.NameElements.Add(nameElement);
                }
                else
                {
                    var number = ReadIdentifierType("Number");
                    if (number != null)
                    {
                        if (element.Numbers == null)
                        {
                            element.Numbers = new List<IdentifierType>();
                        }

                        element.Numbers.Add(number);
                    }
                    else
                    {
                        break;
                    }
                }
            }

            if (reader.NodeType == XmlNodeType.EndElement)
            {
                reader.Read(); // from a:Thoroughfare or a:SubThoroughfare end element to next node
            }

            return element;
        }

        public ThoroughfareNameElement ReadThoroughfareNameElement()
        {
            if (!CheckElement("NameElement"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var value = reader.ReadElementContentAsString();
            var element = new ThoroughfareNameElement(value);
            attrs = ctReader.ReadAbbreviationGroup(element, attrs);
            attrs = ReadThoroughfareNameElementAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            return element;
        }

        public IdentifierType ReadIdentifierType(string elementName)
        {
            if (!CheckElement(elementName))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var value = reader.ReadElementContentAsString();
            var element = new IdentifierType(value);
            attrs = ctReader.ReadAbbreviationGroup(element, attrs);
            attrs = ReadIdentifierTypeAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            return element;
        }

        public Premises ReadPremises()
        {
            if (!CheckElement("Premises"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new Premises();
            attrs = ReadPremisesAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            ReadPremisesTypeNamesAndNumbers(element);
            while (CheckElement("SubPremises"))
            {
                var subElement = ReadSubPremises();
                if (element.SubPremises == null)
                {
                    element.SubPremises = new List<SubPremises>();
                }

                element.SubPremises.Add(subElement);
            }

            reader.Read(); // from a:Premises end element to next node
            return element;
        }

        public SubPremises ReadSubPremises()
        {
            if (!CheckElement("SubPremises"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new SubPremises();
            attrs = ReadSubPremisesAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            ReadPremisesTypeNamesAndNumbers(element);
            reader.Read(); // from a:SubPremises end element to next node
            return element;
        }

        public void ReadPremisesTypeNamesAndNumbers(PremisesType element)
        {
            if (!CheckElement("Premises") && !CheckElement("SubPremises"))
            {
                throw new ArgumentOutOfRangeException();
            }

            reader.Read(); // to a:NameElement or a:Number
            while (true)
            {
                var nameElement = ReadPremisesNameElement();
                if (nameElement != null)
                {
                    if (element.NameElements == null)
                    {
                        element.NameElements = new List<PremisesNameElement>();
                    }

                    element.NameElements.Add(nameElement);
                }
                else
                {
                    var number = ReadIdentifierType("Number");
                    if (number != null)
                    {
                        if (element.Numbers == null)
                        {
                            element.Numbers = new List<IdentifierType>();
                        }

                        element.Numbers.Add(number);
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }

        public PremisesNameElement ReadPremisesNameElement()
        {
            if (!CheckElement("NameElement"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var value = reader.ReadElementContentAsString();
            var element = new PremisesNameElement(value);
            attrs = ctReader.ReadAbbreviationGroup(element, attrs);
            attrs = ReadPremisesNameElementAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            return element;
        }

        public PostCode ReadPostCode()
        {
            if (!CheckElement("PostCode"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new PostCode();
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to first a:Identifier
            while (CheckElement("Identifier"))
            {
                var identifier = ReadIdentifierType("Identifier");
                if (element.Identifiers == null)
                {
                    element.Identifiers = new List<IdentifierType>();
                }

                element.Identifiers.Add(identifier);
            }

            reader.Read(); // from a:PostCode end element to next node
            return element;
        }

        public RuralDelivery ReadRuralDelivery()
        {
            if (!CheckElement("RuralDelivery"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new RuralDelivery();
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ReadRuralDeliveryAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to first a:Identifier
            while (CheckElement("Identifier"))
            {
                var identifier = ReadIdentifierType("Identifier");
                if (element.Identifiers == null)
                {
                    element.Identifiers = new List<IdentifierType>();
                }

                element.Identifiers.Add(identifier);
            }

            reader.Read(); // from a:RuralDelivery end element to next node
            return element;
        }

        public PostalDeliveryPoint ReadPostalDeliveryPoint()
        {
            if (!CheckElement("PostalDeliveryPoint"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new PostalDeliveryPoint();
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ReadPostalDeliveryPointAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to first a:Identifier
            while (CheckElement("Identifier"))
            {
                var identifier = ReadIdentifierType("Identifier");
                if (element.Identifiers == null)
                {
                    element.Identifiers = new List<IdentifierType>();
                }

                element.Identifiers.Add(identifier);
            }

            reader.Read(); // from a:PostalDeliveryPoint end element to next node
            return element;
        }

        public PostOffice ReadPostOffice()
        {
            if (!CheckElement("PostOffice"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new PostOffice();
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ReadPostOfficeAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to first a:Identifier
            while (CheckElement("Identifier"))
            {
                var identifier = ReadIdentifierType("Identifier");
                if (element.Identifiers == null)
                {
                    element.Identifiers = new List<IdentifierType>();
                }

                element.Identifiers.Add(identifier);
            }

            reader.Read(); // from a:PostOffice end element to next node
            return element;
        }

        public IGeoRss ReadGeoRss()
        {
            if (!CheckElement("GeoRSS"))
            {
                return null;
            }
            
            return new GeoRssImpl
            {
                RawXml = reader.ReadInnerXml() // skip a:GeoRss to next element
            };
        }

        public LocationByCoordinates ReadLocationByCoordinates()
        {
            if (!CheckElement("LocationByCoordinates"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new LocationByCoordinates();
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ReadLocationByCoordinatesAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            if (!reader.IsEmptyElement)
            {
                reader.Read(); // to a:Latitude or a:Longitude or a:LocationByCoordinates end element
                if (CheckElement("Latitude"))
                {
                    element.Latitude = ReadLatitude();
                }

                if (CheckElement("Longitude"))
                {
                    element.Longitude = ReadLongitude();
                }
            }

            reader.Read(); // from a:LocationByCoordinates end or empty element to next node
            return element;
        }

        public Latitude ReadLatitude()
        {
            if (!CheckElement("Latitude"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new Latitude();
            attrs = ReadGeographicCoordinateAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // from a:Latitude to next element
            return element;
        }

        public Longitude ReadLongitude()
        {
            if (!CheckElement("Longitude"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new Longitude();
            attrs = ReadGeographicCoordinateAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // from a:Longitude to next element
            return element;
        }

        public IEnumerable<_XmlAttribute> ReadAddressAttributes(
            AddressType element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new AddressTypeList(xmlAttribute.Value);
                            continue;
                        case "AddressID":
                            element.AddressId = new CTString(xmlAttribute.Value);
                            continue;
                        case "AddressIDType":
                            element.AddressIdType = new AddressIdTypeList(xmlAttribute.Value);
                            continue;
                        case "ID":
                            element.Id = new CTString(xmlAttribute.Value);
                            continue;
                        case "Usage":
                            element.Usage = new AddressUsageList(xmlAttribute.Value);
                            continue;
                        case "Status":
                            element.Status = new StatusList(xmlAttribute.Value);
                            continue;
                        case "DeliveryMode":
                            element.DeliveryMode = new DeliveryModeList(xmlAttribute.Value);
                            continue;
                        case "AddressKey":
                            element.AddressKey = new CTString(xmlAttribute.Value);
                            continue;
                        case "AddressKeyRef":
                            element.AddressKeyRef = new CTString(xmlAttribute.Value);
                            continue;
                    }
                }
            
                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadAddressLineAttributes(
            AddressLine element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new AddressLineTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadCountryNameElementAttributes(
            CountryNameElement element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "NameType":
                            element.NameType = new CountryNameTypeList(xmlAttribute.Value);
                            continue;
                        case "NameCode":
                            element.NameCode = new CountryNameCodeList(xmlAttribute.Value);
                            continue;
                        case "NameCodeType":
                            element.NameCodeType = new CTString(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadAdministrativeAreaAttributes(
            AdministrativeArea element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new AdministrativeAreaTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadAdministrativeAreaNameElementAttributes(
            AdministrativeAreaNameElement element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "NameType":
                            element.NameType = new AdministrativeAreaNameTypeList(xmlAttribute.Value);
                            continue;
                        case "NameCode":
                            element.NameCode = new AdministrativeAreaNameCodeList(xmlAttribute.Value);
                            continue;
                        case "NameCodeType":
                            element.NameCodeType = new CTString(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadSubAdministrativeAreaAttributes(
            SubAdministrativeArea element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new SubAdministrativeAreaTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadSubAdministrativeAreaNameElementAttributes(
            SubAdministrativeAreaNameElement element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "NameType":
                            element.NameType = new SubAdministrativeAreaNameTypeList(xmlAttribute.Value);
                            continue;
                        case "NameCode":
                            element.NameCode = new SubAdministrativeAreaNameCodeList(xmlAttribute.Value);
                            continue;
                        case "NameCodeType":
                            element.NameCodeType = new CTString(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadLocalityAttributes(
            Locality element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new LocalityTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadLocalityNameElementAttributes(
            LocalityNameElement element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "NameType":
                            element.NameType = new LocalityNameTypeList(xmlAttribute.Value);
                            continue;
                        case "NameCode":
                            element.NameCode = new LocalityNameCodeList(xmlAttribute.Value);
                            continue;
                        case "NameCodeType":
                            element.NameCodeType = new CTString(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadSubLocalityAttributes(
            SubLocality element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new SubLocalityTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadSubLocalityNameElementAttributes(
            SubLocalityNameElement element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "NameType":
                            element.NameType = new SubLocalityNameTypeList(xmlAttribute.Value);
                            continue;
                        case "NameCode":
                            element.NameCode = new SubLocalityNameCodeList(xmlAttribute.Value);
                            continue;
                        case "NameCodeType":
                            element.NameCodeType = new CTString(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadThoroughfareTypeAttributes(
            ThoroughfareType element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new ThoroughfareTypeList(xmlAttribute.Value);
                            continue;
                        case "TypeCode":
                            element.TypeCode = new CTString(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadThoroughfareNameElementAttributes(
            ThoroughfareNameElement element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "NameType":
                            element.NameType = new ThoroughfareNameTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadIdentifierTypeAttributes(
            IdentifierType element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new IdentifierElementTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadPremisesAttributes(
            Premises element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new PremisesTypeList(xmlAttribute.Value);
                            continue;
                        case "TypeCode":
                            element.TypeCode = new CTString(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadSubPremisesAttributes(
            SubPremises element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new SubPremisesTypeList(xmlAttribute.Value);
                            continue;
                        case "TypeCode":
                            element.TypeCode = new CTString(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadPremisesNameElementAttributes(
            PremisesNameElement element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "NameType":
                            element.NameType = new PremisesElementTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadRuralDeliveryAttributes(
            RuralDelivery element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new RuralDeliveryTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadPostalDeliveryPointAttributes(
            PostalDeliveryPoint element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new PostalDeliveryPointTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadPostOfficeAttributes(
            PostOffice element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new PostOfficeTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadGeographicCoordinateAttributes(
            IGeographicCoordinate element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "DegreesMeasure":
                            element.DegreesMeasure = new CTString(xmlAttribute.Value);
                            continue;
                        case "MinutesMeasure":
                            element.MinutesMeasure = new CTString(xmlAttribute.Value);
                            continue;
                        case "SecondsMeasure":
                            element.SecondsMeasure = new CTString(xmlAttribute.Value);
                            continue;
                        case "Direction":
                            element.Direction = new DirectionTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadLocationByCoordinatesAttributes(
            LocationByCoordinates element,
            IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Meridian":
                            element.Meridian = new MeridianCodeList(xmlAttribute.Value);
                            continue;
                        case "MeridianCodeType":
                            element.MeridianCodeType = new CTString(xmlAttribute.Value);
                            continue;
                        case "Datum":
                            element.Datum = new DatumCodeList(xmlAttribute.Value);
                            continue;
                        case "DatumCodeType":
                            element.DatumCodeType = new CTString(xmlAttribute.Value);
                            continue;
                        case "Projection":
                            element.Projection = new ProjectionCodeList(xmlAttribute.Value);
                            continue;
                        case "ProjectionCodeType":
                            element.ProjectionCodeType = new CTString(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public bool CheckElement(string name)
        {
            return CheckElement(name, XmlNS);
        }

        public bool CheckElement(string name, string ns)
        {
            var elem = reader.NodeType == XmlNodeType.Element ? reader.LocalName : null;
            return elem != null && reader.NamespaceURI == ns && elem == name;
        }

        private class GeoRssImpl : IGeoRss
        {
            public string RawXml { get; set; }
        }
    }
}